#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include "detector/base_detector.h"
#include <filesystem>
#include "otl_timer.h"
#include "otl_string.h"
#include <thread>
#include "nlohmann/json.hpp"

namespace  fs = std::filesystem;
using json = nlohmann::json;

// 绘制检测框
void draw_detections(cv::Mat& frame, const std::vector<DetectionResult>& results, const std::vector<std::string>& class_names) {
    for (const auto& det : results) {
        cv::rectangle(frame, det.bbox, cv::Scalar(0, 255, 0), 2);
        std::string label = class_names[det.class_id] + ": " + std::to_string(det.confidence);
        cv::putText(frame, label, det.bbox.tl(), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 255), 2);
    }
}

// Get all input files from specified paths
std::vector<std::string> getInputFiles(const std::vector<std::string>& paths) {
    std::vector<std::string> files;

    for (const auto& path : paths) {
        if (fs::is_directory(path)) {
            // If path is a directory, traverse all files
            for (const auto& entry : fs::directory_iterator(path)) {
                if (entry.is_regular_file()) {
                    files.push_back(entry.path().string());
                }
            }
        } else if (fs::is_regular_file(path)) {
            // If path is a file, add it directly
            files.push_back(path);
        } else {
            std::cerr << "Warning: Path does not exist or is not a valid file/directory: " << path << std::endl;
        }
    }

    if (files.empty()) {
        std::cerr << "Error: No valid input files found" << std::endl;
        exit(EXIT_FAILURE);
    }

    return files;
}

struct DetectChannel
{
    std::shared_ptr<BaseDetector> detector;
    otl::StatToolPtr fpsStat;
    std::thread *thread{nullptr};
    std::vector<std::string> inputFiles;
    std::vector<cv::Mat> images;
    std::vector<DetectionResult> detection_results;

    DetectChannel() {
        fpsStat = otl::StatTool::create(5);
    }

    virtual ~DetectChannel() {

    }

};


int main(int argc, char** argv) {
    std::string configFile = "config.json";
    if (argc > 1) {
        configFile = argv[1];
    }

    // 打开并读取文件
    if (!fs::exists(configFile))
    {
        if (!fs::exists("../../detect_perf/config.json"))
        {
            std::cerr << "cofnig.json不存在 " << std::endl;
            return false;

        }
        configFile = "../../detect_perf/config.json";
    }
    std::ifstream file(configFile);
    if (!file.is_open()) {
        std::cerr << "无法打开配置文件: " << configFile << std::endl;
        return false;
    }

    // 解析JSON
    json configsJson;
    try {
        file >> configsJson;
    } catch (const json::exception& e) {
        std::cerr << "Error parsing config.json: " << e.what() << std::endl;
        return -1;
    }

    int card_num = configsJson["dev_num"].get<int>();
    std::string datasets_path = configsJson["datasets"].get<std::string>();
    if (otl::startWith(datasets_path, "~/"))
    {
        datasets_path.replace(0,1, getenv("HOME"));
    }

    std::string model_path = configsJson["model_path"].get<std::string>();
    if (otl::startWith(model_path, "~/"))
    {
        model_path.replace(0,1, getenv("HOME"));
    }

    int chan_num = configsJson["num_per_device"].get<int>();

    otl::TimerQueuePtr tqp = otl::TimerQueue::create();

    std::vector<DetectChannel*> channels;

    for (int devIdx = 0; devIdx < card_num; ++devIdx)
    {
        for (int i = 0; i< chan_num; ++i) {
            DetectChannel *chn = new DetectChannel;
            chn->detector.reset(BaseDetector::CreateDetector(model_path, devIdx, i));
            channels.push_back(chn);

            //
            // Get all input files
            std::vector<std::string> paths;
            paths.push_back(datasets_path);
            std::vector<std::string> inputFiles = getInputFiles(paths);

            for (const auto& file : inputFiles) {
                        cv::Mat image = cv::imread(file);
                        if (image.empty()) {
                            std::cerr << "Warning: Could not read image: " << file << std::endl;
                            continue;
                        }
                        chn->images.push_back(image);
            }
            
            chn->thread = new std::thread([chn]
            {
                int i= 0;
                while (1) {
                    i = i% chn->images.size(); 
                    auto results = chn->detector->detect(chn->images[i]);
                        //chn.detection_results.push_back(results);
                        //printf("detected %d objs\n", results.size());
                    chn->fpsStat->update();
                    i++;
                }
                
            });
        }

        otl::msleep(5000);
    }

    // 等待用户输入以停止
    uint64_t timerId;
    tqp->createTimer(1000, 1, [channels] (){
        double total = 0.0;
        for (auto chan : channels) {
            auto fps = chan->fpsStat->getSpeed();
            total += fps;
        }

        std::cout << otl::formatString("ch-num=%d FPS=%.2f\n", channels.size(), total);
        std::cout.flush();
    }, -1, &timerId);

    tqp->runLoop();

}
